<?php

namespace app\controllers;


use app\exceptions\ApiAccountMissingException;
use app\models\ApiResponseCode;
use app\models\Open;
use app\models\Order;
use app\models\SyncTable;
use yii\web\Controller;

/**
 * 为福利通所提供的接口(钉钉)
 * Class ApiDingTalkController
 * @package app\controllers
 */
class ApiWalfareController extends Controller
{
    public $enableCsrfValidation = false;
    public $layout = false;

    private $_request = null;
    private $_openConfig = null;

    const CacheKeyPrefix = 'api_walfare.cache_key.';
    const CacheDuration = 600; // 10minute

    public function actionTest()
    {
        return $this->renderJson('hello');
    }

    /**
     * 根据机构编码获取机构所有分类信息(用于同步)
     * @return string
     */
    public function actionGetGoodsCategory()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getGoodsCategory($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取某机构所有商品信息(用于同步)
     * @return string
     */
    public function actionGetGoods()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getGoods($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取商品分类与商品关联关系表
     * @return string
     */
    public function actionGetGoodsCategoryGoods()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getGoodsCategoryGoods($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取商品详情信息
     * @return string
     */
    public function actionGetGoodsDetailHtml()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getGoodsDetailHtml($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取机构商品图片
     * @return string
     */
    public function actionGetGoodsImage()
    {
        $items = SyncTable::getGoodsImage();
        return $this->renderJson($items);
    }

    /**
     * 获取机构所有扩展分类,以及分类下对应的商品
     * @return string
     */
    public function actionGetGoodsExtendCategory()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getGoodsExtendCategory($institutionCode);
        foreach ($items as &$item) {
            $item['goods'] = SyncTable::getGoodsExtendCategoryGoodsByCategory($item['id']);
        }
        return $this->renderJson($items);
    }

    /**
     * 获取机构广告信息
     * @return string
     */
    public function actionGetAd()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getAd($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取机构广告分类信息
     * @return string
     */
    public function actionGetAdCategory()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getAdCategory($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取机构特价活动
     * @return string
     */
    public function actionGetActivityBargain()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getActivityBargain($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 获取机构特价活动商品
     * @return string
     */
    public function actionGetActivityBargainGoods()
    {
        $institutionCode = $this->_getInstitutionCode();
        $items = SyncTable::getActivityBargainGoods($institutionCode);
        return $this->renderJson($items);
    }

    /**
     * 根据传入数据生成订单(可包含现金支付)
     * @return string
     */
    public function actionAddOrderWithOpenAndCash()
    {
        $institutionCode = $this->_getInstitutionCode();
        $receiver = (array)$this->_getRequestValue('receiver');
        $goodsList = (array)$this->_getRequestValue('goods_list');
        $remark = $this->_getRequestValue('order_remark', '');
        $tag = $this->_getRequestValue('tag', '');
        $idcardName = $this->_getRequestValue('idcard_name', '');
        $idcardNumber = $this->_getRequestValue('idcard_number', '');
        $cash = $this->_getRequestValue('cash', []);

        $data = [
            'institution_code' => $institutionCode,
            'receiver' => $receiver,
            'goods_list' => $goodsList,
            'remark' => $remark,
            'tag' => $tag,
            'idcardName' => $idcardName,
            'idcardNumber' => $idcardNumber,
            'cash' => $cash
        ];

        try {
            $item = Order::addOrderWithOpenAndCash($data);
            return $this->renderJson($item);
        } catch (\Exception $ex) {
            switch ($ex->getCode()) {
                case 100: // 未找到订单中此商品
                    return $this->renderJson(null, ApiResponseCode::OrderNotFoundGoodsErrorCode, $ex->getMessage());
                    break;
                case 200:  // 订单中的商品销售价异常(销售价为0)
                    return $this->renderJson(null, ApiResponseCode::OrderSellingPriceZeroErrorCode, ApiResponseCode::OrderSellingPriceZeroErrorMessage);
                    break;
                case 300:  // 订单中的商品销售价与实际销售价不符
                    return $this->renderJson(null, ApiResponseCode::OrderSellingPriceErrorCode, ApiResponseCode::OrderSellingPriceErrorMessage);
                    break;
                case 400:  // 跨境商品需要身份证信息
                    return $this->renderJson(null, ApiResponseCode::OrderCrossborderGoodsNeedIDCardInfoErrorCode, ApiResponseCode::OrderCrossborderGoodsNeedIDCardInfoErrorMessage);
                    break;
                default:
                    return $this->renderJson(null, ApiResponseCode::OtherErrorCode, ApiResponseCode::OtherErrorMessage);
                    break;
            }
        }
    }

    /**
     * 根据传入的钉钉系统订单号来查询这些订单在三方中的状态
     * @return string
     */
    public function actionGetOrderStatusList()
    {
        $walfareOrderCodeList = $this->_getRequestValue('codes', []);

        $items = Order::getOrderStatusList($walfareOrderCodeList);

        return $this->renderJson($items);
    }

    /**
     * 根据钉钉的订单编号查询此订单的配送信息
     * @return string
     */
    public function actionGetOrderDeliveryInfo()
    {
        $orderCode = $this->_getRequestValue('order_code');
        $items = Order::getOrderDeliveryInfo($orderCode);
        return $this->renderJson($items);
    }

    /**
     * 根据配送单id查询配送单中包含的商品列表
     * @return string
     */
    public function actionGetDeliveryGoodsList()
    {
        $deliveryId = $this->_getRequestValue('delivery_id');
        $items = Order::getDeliveryGoodsList($deliveryId);
        return $this->renderJson($items);
    }

    public function beforeAction($action)
    {
//        $isRecordLog = \Yii::$app->params['isRecordLog'];
//        if ($isRecordLog == true) {
//            // 记录请求日志
//            $logId = ApiOperateLog::add(
//                $action->id,
//                $_GET,
//                $_POST,
//                \Yii::$app->request->get('account'),
//                \Yii::$app->request->get('timestamp'),
//                \Yii::$app->request->get('sign'),
//                $_SERVER['REMOTE_ADDR'],
//                $_SERVER['REQUEST_TIME']
//            );
//            \Yii::$app->session->set('request_api_operate_log_id', $logId);
//            \Yii::$app->session->set('request_api_timestamp', Tools::getMillisecond());
//        }

        // if (YII_ENV != 'dev') {
        if (!$this->_auth()) {
            return false;
        }
        // }

        $this->_request = json_decode(file_get_contents("php://input"));
        return parent::beforeAction($action);
    }

    /**
     * 重写基类runAction,已处理所有action异常
     * @param string $id
     * @param array $params
     * @return mixed|void
     */
    public function runAction($id, $params = [])
    {
        try {
            return parent::runAction($id, $params);
        } catch (\Exception $ex) {
//            $isRecordLog = \Yii::$app->params['isRecordLog'];
//            if ($isRecordLog == true) {
//                // 记录异常日志
//                $logId = \Yii::$app->session->get('request_api_operate_log_id');
//                if ($logId != null) {
//                    ApiOperateLog::setException($logId, $ex);
//
//                    $operateLog = ApiOperateLog::getById($logId);
//                    if ($operateLog != null) {
//                        ApiExceptionLog::add(
//                            $operateLog->actionId,
//                            $operateLog->getParams,
//                            $operateLog->postParams,
//                            $operateLog->account,
//                            $operateLog->timestamp,
//                            $operateLog->sign,
//                            $operateLog->remoteAddr,
//                            $operateLog->createDatetime,
//                            $ex
//                        );
//                    }
//                }
//            }

            return $this->_errorHandler($ex);
        }
    }

    public function afterAction($action, $result)
    {
//        $isRecordLog = \Yii::$app->params['isRecordLog'];
//        if ($isRecordLog == true) {
//            // 记录请求总共耗时(毫秒)
//            $logId = \Yii::$app->session->get('request_api_operate_log_id');
//            if ($logId != null) {
//                $time = Tools::getMillisecond();
//                $time = $time - \Yii::$app->session->get('request_api_timestamp');
//                ApiOperateLog::setDurationTime($logId, $time);
//            }
//        }

        return parent::afterAction($action, $result);
    }

    /**
     * 自定义的错误处理返回json
     * @param \Exception $ex
     * @return string
     */
    private function _errorHandler($ex)
    {
        try {
            throw $ex;
        } catch (ApiAccountMissingException $e) {
            if (YII_DEBUG == true) {
                return $this->renderJson(null, ApiResponseCode::AccountMissingCode, ApiResponseCode::AccountMissingMessage);
            } else {
                return $this->renderJson(null, ApiResponseCode::ExceptionCode, ApiResponseCode::ExceptionMessage . ':' . $ex->getMessage());
            }
        } catch (\Exception $e) {
            if (YII_DEBUG == true) {
                return $this->renderJson(null, ApiResponseCode::ExceptionCode, ApiResponseCode::ExceptionMessage . ':' . $ex->getMessage());
            } else {
                return $this->renderJson(null, ApiResponseCode::ExceptionCode, ApiResponseCode::ExceptionMessage . ':' . $ex->getMessage());
            }
        }
    }

    /**
     * 接口返回通用数据结构
     * @param array|mixed $data
     * @param int $status [0:success]
     * @param string $message
     * @return string
     */
    public function renderJson($data = null, $status = 0, $message = 'success')
    {
        $result = [
            'status' => $status,
            'message' => $message,
            'data' => $data
        ];

        return json_encode($result, true);
    }

    /**
     * 接口签名验证
     * @return bool
     */
    private function _auth()
    {
        // 缺少account参数
        $account = \Yii::$app->request->get('account');
        if ($account == null) {
            $this->_logException(new \Exception(ApiResponseCode::AccountMissingMessage, ApiResponseCode::AccountMissingCode));
            echo $this->renderJson(null, ApiResponseCode::AccountMissingCode, ApiResponseCode::AccountMissingMessage);  // 缺少账户信息 (1001)
            return false;
        }

        // 缺少timestamp参数
        $timestamp = \Yii::$app->request->get('timestamp');
        if ($timestamp == null) {
            $this->_logException(new \Exception(ApiResponseCode::TimestampMissingMessage, ApiResponseCode::TimestampMissingCode));
            echo $this->renderJson(null, ApiResponseCode::TimestampMissingCode, ApiResponseCode::TimestampMissingMessage);  // 缺少时间戳
            return false;
        }

        // 缺少verify参数
        $verify = \Yii::$app->request->get('verify');
        if ($verify == null) {
            $this->_logException(new \Exception(ApiResponseCode::VerifyMissingMessage, ApiResponseCode::VerifyMissingCode));
            echo $this->renderJson(null, ApiResponseCode::VerifyMissingCode, ApiResponseCode::VerifyMissingMessage);
            return false;
        }

        // 缺少sign参数
        $sign = \Yii::$app->request->get('sign');
        if ($sign == null) {
            $this->_logException(new \Exception(ApiResponseCode::SignMissingMessage, ApiResponseCode::SignMissingCode));
            echo $this->renderJson(null, ApiResponseCode::SignMissingCode, ApiResponseCode::SignMissingMessage);  // 缺少签名信息
            return false;
        }

        // timestamp过期
        if (abs(time() - $timestamp) > \Yii::$app->params['api.TimestampValidity']) {
            $this->_logException(new \Exception(ApiResponseCode::TimestampInvalidMessage, ApiResponseCode::TimestampInvalidCode));
            echo $this->renderJson(null, ApiResponseCode::TimestampInvalidCode, ApiResponseCode::TimestampInvalidMessage);  // 签名过期
            return false;
        }

        // sign认证
        $token = $this->_getAccountToken();
        $authSign = hash_hmac('sha256', $account . $token . $timestamp . $verify, $token);
        if ($authSign != $sign) {
            $this->_logException(new \Exception(ApiResponseCode::SignAuthFailedMessage, ApiResponseCode::SignAuthFailedCode));
            echo $this->renderJson(null, ApiResponseCode::SignAuthFailedCode, ApiResponseCode::SignAuthFailedMessage);  // 签名认证失败
            return false;
        }

        // 请求校验
        if ($this->_checkVerify($account, $verify) == false) {
            $this->_logException(new \Exception(ApiResponseCode::VerifyInvalidMessage, ApiResponseCode::VerifyInvalidCode));
            echo $this->renderJson(null, ApiResponseCode::VerifyInvalidCode, ApiResponseCode::VerifyInvalidMessage);
            return false;
        }

        return true;
    }

    /**
     * 校验verify。如果已经存在此verify,则此请求校验信息无效。如果不存在verify,则记录此verify。
     * @param $account
     * @param $verify
     * @return bool
     */
    private function _checkVerify($account, $verify)
    {
        $key = 'api_verify:' . $account . $verify;
        if (\Yii::$app->cache->exists($key)) {
            return false;
        } else {
            \Yii::$app->cache->set($key, null, 65);
            return true;
        }
    }

    /**
     * 根据account获取对应的open config
     * @return mixed|null
     * @throws ApiAccountMissingException
     */
    private function _getOpenConfig()
    {
        // 缺少account参数
        $account = \Yii::$app->request->get('account');
        if ($account == null) {
            throw new ApiAccountMissingException();
//            echo $this->renderJson(null, ApiResponseCode::AccountMissingCode, ApiResponseCode::AccountMissingMessage);  // 缺少账户信息 (1001)
//            return null;
        }

        $cacheKey = self::CacheKeyPrefix . '_getOpenConfig:' . $account;
        if ($this->_openConfig == null) {
            $this->_openConfig = \Yii::$app->cache->get($cacheKey);
            if ($this->_openConfig == false) {
                $this->_openConfig = null;
            }
        }

        if ($this->_openConfig != null) {
            if ($this->_openConfig['account'] != $account) {
                $this->_openConfig = null;
            }
        }
        if ($this->_openConfig == null) {
            $this->_openConfig = Open::getConfigByAccount($account);
            \Yii::$app->cache->set($cacheKey, $this->_openConfig, self::CacheDuration);
        }
        return $this->_openConfig;
    }

    /**
     * 获取account的token
     * @return string
     */
    private function _getAccountToken()
    {
        $openConfig = $this->_getOpenConfig();
        if ($openConfig == null) {
            return '';
        }

        $token = $openConfig['token'];
        return $token;
    }

    /**
     * 根据account获取对应的机构编码
     * @return bool|string
     * @throws \Exception
     */
    private function _getInstitutionCode()
    {
        $openConfig = $this->_getOpenConfig();
        if ($openConfig == null) {
            return '';
        }

        if ($openConfig['is_agent'] == 0) {  // 如果此配置非代理商,那么机构编码取account所对于的机构。
            $institutionCode = $openConfig['institution_code'];
        } else {  // 如果此配置为代理商配置,那么机构编码取post传入进来的institution_code参数,并验证此institution_code为此代理商或此代理商的旗下机构。
            $configInstitutionCode = $openConfig['institution_code'];
            $institutionCode = $this->_getRequestValue('institution_code');
            if ($institutionCode != $configInstitutionCode) {
                $cacheKey = self::CacheKeyPrefix . 'Institution::getInstitutionByCode:' . $institutionCode;
                $t = \Yii::$app->cache->get($cacheKey);
                if ($t == false) {
                    $t = Institution::getInstitutionByCode($institutionCode);
                    \Yii::$app->cache->set($cacheKey, $t, self::CacheDuration);
                }
                if ($t['parent_code'] != $configInstitutionCode) {
                    $institutionCode = '';
                }
            }
        }
        return $institutionCode;
    }

    /**
     * 获取post传入进来的某个参数值
     * @param $request
     * @param string $paramName 参数名
     * @param null|string|int|float $defaultValue
     * @return mixed|null
     */
    private function _getRequestParamValue($request, $paramName, $defaultValue = null)
    {
        $returnValue = null;
        $request = (array)$request;
        if (isset($request[$paramName])) {
            $returnValue = $request[$paramName];
        }
        if ($returnValue == null) {
            if (isset($defaultValue)) {
                $returnValue = $defaultValue;
            } else {
                echo $this->renderJson(null, ApiResponseCode::ParamMissingCode, ApiResponseCode::ParamMissingMessage . ': ' . $paramName);
                exit;
            }
        }
        return $returnValue;
    }

    /**
     * 获取当前这次post传入进来的某个参数值
     * @param string $paramName 参数名
     * @param null|string|int|float $defaultValue
     * @return mixed|null
     */
    private function _getRequestValue($paramName, $defaultValue = null)
    {
        return $this->_getRequestParamValue($this->_request, $paramName, $defaultValue);
    }

    /**
     * 验证用户ticket
     * @return int userId
     * @throws \Exception
     */
    private function _authUserTicket()
    {
        $userTicket = $this->_getRequestValue('user_ticket');
        $userId = User::authUserTicket($userTicket);
//        $userId = '2';
        if ($userId == null) {
            $this->_logException(new \Exception(ApiResponseCode::UserTicketErrorMessage, ApiResponseCode::UserTicketErrorCode));
            echo $this->renderJson(null, ApiResponseCode::UserTicketErrorCode, ApiResponseCode::UserTicketErrorMessage);
            exit;
        }
        return $userId;
    }

    /**
     * 记录当前会话的异常日志
     * @param $ex
     */
    private function _logException($ex)
    {
//        $isRecordLog = \Yii::$app->params['isRecordLog'];
//        if ($isRecordLog == true) {
//            // 记录异常日志
//            $logId = \Yii::$app->session->get('request_api_operate_log_id');
//            if ($logId != null) {
//                ApiOperateLog::setException($logId, $ex);
//
//                $operateLog = ApiOperateLog::getById($logId);
//                if ($operateLog != null) {
//                    ApiExceptionLog::add(
//                        $operateLog->actionId,
//                        $operateLog->getParams,
//                        $operateLog->postParams,
//                        $operateLog->account,
//                        $operateLog->timestamp,
//                        $operateLog->sign,
//                        $operateLog->remoteAddr,
//                        $operateLog->createDatetime,
//                        $ex
//                    );
//                }
//            }
//        }
    }
}